In [1]:
from IPython.display import display
from IPython.display import HTML
import IPython.core.display as di # Example: di.display_html('<h3>%s:</h3>' % str, raw=True)

# This line will hide code by default when the notebook is exported as HTML
di.display_html('<script>jQuery(function() {if (jQuery("body.notebook_app").length == 0) { jQuery(".input_area").toggle(); jQuery(".prompt").toggle();}});</script>', raw=True)

# This line will add a button to toggle visibility of code blocks, for use with the HTML export version
di.display_html('''<button onclick="jQuery('.input_area').toggle(); jQuery('.prompt').toggle();">Toggle code</button>''', raw=True)
In [1]:
%reset
#pylab qt -> open windows, needs display
%pylab qt 
%load_ext autoreload
%autoreload 2
Once deleted, variables cannot be recovered. Proceed (y/[n])? y
Populating the interactive namespace from numpy and matplotlib
In [2]:
#from pySurf.fit_cylinder import *
#from pySurf.points import *
from pySurf.points import plot_points,matrix_to_points2
from pySurf.points import subtract_points,level_points
from pySurf.points import points_find_hull,crop_points
from pySurf.psd2d import calculatePSD
from pySurf.points import points_autoresample
#from calibrate_align import *
from pyProfile.profile import polyfit_profile
from plotting.multiplots import compare_images
from dataIO.fn_add_subfix import fn_add_subfix
from dataIO.span import span
#from PSDanalysis import *
from pySurf.instrumentReader import matrix4D_reader
import os
from pySurf.instrumentReader import bin_reader
from plotting.add_clickable_markers import add_clickable_markers
from plotting.backends import maximize
In [3]:
from pySurf.instrumentReader import points_reader
from pySurf.affine2D import plot_transform,find_rototrans
from pySurf.distanceTable import distanceTable
from pySurf.data2D_class import Data2D
from pySurf.data2D import data_from_txt
from dataIO.outliers import remove_outliers
In [4]:
%qtconsole
In [5]:
pwd
Out[5]:
'G:\\My Drive\\POOL\\PSD\\analysis_PSD'
In [6]:
from pySurf.psd2d import calculatePSD
from utilities.imaging import fitting as fit
In [7]:
correct_misalignment=fit.fitConeMisalign

Determination of PZT stress

In [8]:
#2019/03/19 C1S04_PZT_stress_fit from C1S05_stress_compensated_WFS_fit
#  fit PZT stress on first measuremnt of sample received from PSU, 
#    gold coating applied at PSU.

outfolder=r'results\C1S04_PZT_stress_fit'  #set output

WFS Data

WFS data are utilized because the deformation due to PZT stress is likely to be out of the 4D interferomenter dynamic range. Data are presented below:

Bare Substrate

In [28]:
wf=r'G:\My Drive\POOL\PSD\analysis_PSD\results\C1S04_cut\180206_C1S04_GentexCut_Meas3_matrix.dat'
p1=Data2D(file=wf,reader=data_from_txt,delimiter=' ',units=['mm','mm','$\mu$m'],
         name='C1S04 substrate WFS data',addaxis=1,center=(0,0))
In [29]:
plt.figure(1)
plt.clf()
p1.level().plot()
display(plt.gcf())

After PZT deposition

In [30]:
wf=r'G:\My Drive\POOL\PSD\analysis_PSD\results\C1S04_PZT\190314_06_C1S04.dat'
In [31]:
p2=Data2D(file=wf,reader=data_from_txt,delimiter=' ',units=['mm','mm','$\mu$m'],
         name='C1S04 PZT WFS',addaxis=True,center=(0,0))
p2=p2.remove_nan_frame()
In [32]:
plt.figure(1)
plt.clf()
p2.plot()
display(plt.gcf())

Align and transform

Data are aligned by interactively selecting recognizeable features and dimples. An affine transformation is employed to correct potential difference of horizontal scale.

Picture below shows data before and after PZT with first 2 2D Legendre terms removed.

In [33]:
plt.close('all')
plt.figure()
maximize()

ax1=plt.subplot(121)
p1.level(2).plot()
plt.clim([-1,1])

ax2=plt.subplot(122,sharex=ax1,sharey=ax1)
p2.level(2).plot()
plt.clim([-1,1])

plt.suptitle('2nd order 2D legendre removed')

display(plt.gcf())
In [34]:
plt.savefig(os.path.join(outfolder,'data_comparison_2leg.png'))

Here same data with sag removed by line, as used for feature identification and alignment:

In [35]:
plt.figure()
maximize()

ax1=plt.subplot(121)
p1.level(2,byline=True).plot()
plt.clim(*remove_outliers(p1.level(2,byline=True).data,span=True,
                         nsigma=2,itmax=3))

ax2=plt.subplot(122,sharex=ax1,sharey=ax1)
p2.level(2,byline=True).plot()
plt.clim(*remove_outliers(p2.level(2,byline=True).data,span=True,
                         nsigma=2,itmax=3))

plt.suptitle('Y sag removed by line - natural color scale')

display(plt.gcf())
G:\My Drive\libraries\python\userKov3\dataIO\outliers.py:28: RuntimeWarning: invalid value encountered in less
  mask=mask & (np.abs(data-np.nanmean(data))<(nsigma*sigma))
In [36]:
plt.savefig(os.path.join(outfolder,'data_comparison_sag.png'))

Align Data

In [19]:
from plotting.add_clickable_markers import add_clickable_markers2

add_clickable_markers2(ax=ax1)
add_clickable_markers2(ax=ax2)
cids established:
 [5, 6, 7]
cids established:
 [8, 9, 10]
Out[19]:
<matplotlib.axes._subplots.AxesSubplot at 0x25e514595c0>

These values are the markers positions:

In [40]:
print(ax1.markers)
print(ax2.markers)
[[ 24.58277715  21.49717816]
 [ 19.29575731  28.96120617]
 [ -3.25182729  28.80570559]
 [ 18.05175264 -11.62444612]
 [ 10.12122289 -17.37796771]
 [-46.32548891  32.84872076]
 [-38.39495916  41.40125285]
 [-43.05997666 -26.08600038]]
[[-20.1588192  -20.47815281]
 [-14.40805715 -27.93860088]
 [  7.50700904 -27.62774887]
 [-15.34061316  12.62758548]
 [ -6.94760908  18.68919953]
 [ 45.43095338 -30.7362689 ]
 [ 39.05848732 -40.06182898]
 [ 41.70072935  26.3050736 ]]
In [37]:
m1=np.array([[24.58277715001921, 21.497178162325042], [19.29575731191258, 28.96120616906383], [-3.251827291777502, 28.805705585590104], [18.051752644122786, -11.624446117578294], [10.121222886962826, -17.377967706106105], [-46.32548891399922, 32.84872075590694], [-38.39495915683926, 41.401252846961796], [-43.059976661051, -26.086000380634687]])
m2=np.array([[-20.15881919858859, -20.478152809990057], [-14.408057148043014, -27.9386008755627], [7.507009044576591, -27.62774887283051], [-15.340613156239613, 12.627585480988529], [-6.947609082470393, 18.689199534266308], [45.43095337790416, -30.73626890015244], [39.05848732189423, -40.06182898211824], [41.70072934511788, 26.305073601205038]])

To verify the scale of the images, the three tables below report the distance of each marker from all the others on same surface data.

These are reported respectively for each of the data. The third table is the difference in corresponding distances.

Picture below shows the position of the fiducials used for alignment.

In [41]:
from pySurf.distanceTable import distanceTable
#import print_options
#print_options.set_float_precision(2)
print(distanceTable(m1))
print('---------------------------')
print(distanceTable(m2))
print('---------------------------')
print(distanceTable(m1)-distanceTable(m2))
[[ 0.          9.14681873 28.77811282 33.75938797 41.47786781 71.81113922
  66.04822071 82.70248499]
 [ 9.14681873  0.         22.54812081 40.60471302 47.23866126 65.73629687
  59.01672244 83.17711529]
 [28.77811282 22.54812081  0.         45.6994495  48.08087093 43.26298992
  37.33212461 67.80699182]
 [33.75938797 40.60471302 45.6994495   0.          9.79777081 78.24507526
  77.44647199 62.79952237]
 [41.47786781 47.23866126 48.08087093  9.79777081  0.         75.5576039
  76.21559348 53.88942214]
 [71.81113922 65.73629687 43.26298992 78.24507526 75.5576039   0.
  11.66358039 59.02512114]
 [66.04822071 59.01672244 37.33212461 77.44647199 76.21559348 11.66358039
   0.         67.64829441]
 [82.70248499 83.17711529 67.80699182 62.79952237 53.88942214 59.02512114
  67.64829441  0.        ]]
---------------------------
[[ 0.          9.41963638 28.57472268 33.45452163 41.33542745 66.38710125
  62.37154611 77.55819763]
 [ 9.41963638  0.         21.91727071 40.57690398 47.22086463 59.90437486
  54.82375432 78.04211775]
 [28.57472268 21.91727071  0.         46.28720973 48.52005456 38.05112943
  33.9131557  63.85890578]
 [33.45452163 40.57690398 46.28720973  0.         10.35305184 74.65659493
  75.73266488 58.65823417]
 [41.33542745 47.22086463 48.52005456 10.35305184  0.         72.01660042
  74.62066911 49.24086077]
 [66.38710125 59.90437486 38.05112943 74.65659493 72.01660042  0.
  11.29488355 57.16318156]
 [62.37154611 54.82375432 33.9131557  75.73266488 74.62066911 11.29488355
   0.         66.41947908]
 [77.55819763 78.04211775 63.85890578 58.65823417 49.24086077 57.16318156
  66.41947908  0.        ]]
---------------------------
[[ 0.         -0.27281765  0.20339014  0.30486634  0.14244036  5.42403797
   3.6766746   5.14428736]
 [-0.27281765  0.          0.6308501   0.02780904  0.01779663  5.83192201
   4.19296813  5.13499754]
 [ 0.20339014  0.6308501   0.         -0.58776022 -0.43918363  5.2118605
   3.41896891  3.94808604]
 [ 0.30486634  0.02780904 -0.58776022  0.         -0.55528102  3.58848033
   1.71380711  4.14128821]
 [ 0.14244036  0.01779663 -0.43918363 -0.55528102  0.          3.54100348
   1.59492437  4.64856137]
 [ 5.42403797  5.83192201  5.2118605   3.58848033  3.54100348  0.
   0.36869683  1.86193958]
 [ 3.6766746   4.19296813  3.41896891  1.71380711  1.59492437  0.36869683
   0.          1.22881533]
 [ 5.14428736  5.13499754  3.94808604  4.14128821  4.64856137  1.86193958
   1.22881533  0.        ]]
In [42]:
plt.close('all')
plt.figure()
maximize()

plt.suptitle('2 legendre removed by line for alignment')
ax1=plt.subplot(121)
p1.level(2,byline=True).plot()
plt.clim([-1,1])

ax2=plt.subplot(122,sharex=ax1,sharey=ax1)
p2.level(2,byline=True).plot()
plt.clim([-1,1])

for a,m in zip([ax1,ax2],[m1,m2]):
    plt.sca(a)
    for mm in m:
        plot(mm[0],mm[1],'rx')

display(plt.gcf())
In [24]:
plt.savefig(os.path.join(outfolder,'data_markers.png'))

Transformation

In [43]:
import logging

try: 
    logger
except NameError:
    # create logger
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)

    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    # add formatter to ch
    ch.setFormatter(formatter)

    # add ch to logger
    logger.addHandler(ch)

Two transformations are chosen: rototranslation and affine transformation, that transform second set of markers (on sample after PZT) to the other.

In an ideal case, transforming markers should bring to exact same positions on first untransformed data. Errors on markers position, respectively with rototranslation and affine tranformation, are reported below. Errors include optical (metrology) effects and the inaccuracy in the selection of exact marker location.

Affine transformation is clearly more accurate and it will be used to align the two data (the second one is transformed).

In [44]:
from pySurf.affine2D import find_affine
trans=find_rototrans(m2,m1)
transa=find_affine(m2,m1)
2019-03-19 20:01:00,619 - root - INFO - stdv of markers distance errors from barycenter: 1.1071986752610796
2019-03-19 20:01:00,620 - root - INFO - rotation angle (degrees): -178.82822619345555 +-2.6343404566051327
2019-03-19 20:01:00,624 - root - INFO - translation: [ 16.97785282 -23.45579889]
2019-03-19 20:01:00,628 - root - INFO - errors in markers position after rotations: 
[[ 2.00680829  0.47903409]
 [ 2.3167826   0.36657174]
 [ 1.68603818  0.07369757]
 [ 0.9699908   0.35769325]
 [ 1.55466928  0.49288213]
 [-3.53517868  0.23330245]
 [-2.16648899 -0.40745917]
 [-2.83262147 -1.59572206]]
2019-03-19 20:01:00,629 - root - INFO - mean of error abs val: 
2.2199646506357555
G:\My Drive\libraries\python\userKov3\pySurf\affine2D.py:186: FutureWarning: `rcond` parameter will change to the default of machine precision times ``max(M, N)`` where M and N are the input matrix dimensions.
To use the future default and silence this warning we advise to pass `rcond=None`, to keep using the old, explicitly pass `rcond=-1`.
  A, res, rank, s = np.linalg.lstsq(X, Y)
2019-03-19 20:01:00,653 - root - INFO - - INPUT -
2019-03-19 20:01:00,655 - root - INFO - stdv of markers distance errors from barycenter: 1.1071986752610796
2019-03-19 20:01:00,663 - root - INFO - translation: [ 16.97785282 -23.45579889]
2019-03-19 20:01:00,664 - root - INFO - - OUTPUT -
2019-03-19 20:01:00,668 - root - INFO - Affine Transformation Matrix:
 [[-1.08236978e+00  1.77655912e-03  5.18998225e-18]
 [-3.10146058e-02 -1.01137902e+00  2.71843236e-17]
 [ 2.67795724e+00  1.00664355e+00  1.00000000e+00]]
2019-03-19 20:01:00,669 - root - INFO - stdv of markers distance errors from barycenter: 0.8904166139697417
2019-03-19 20:01:00,673 - root - INFO - translation of barycenter: [ 16.97785282 -23.45579889]
2019-03-19 20:01:00,675 - root - INFO - errors in markers position after transformation: 
[[-0.5495987  -0.18482617]
 [ 0.15644969 -0.27635539]
 [ 1.33871149 -0.15640019]
 [-0.83878115  0.16743887]
 [ 0.50302167  0.5295959 ]
 [-0.78362823  0.67534891]
 [-0.03969175 -0.19247376]
 [ 0.21351699 -0.56232817]]
2019-03-19 20:01:00,679 - root - INFO - mean of error abs val: 
0.7079355586797249
In [45]:
p2t=p2.apply_transform(transa)
mm2=transa(m2)

Table of distances with transformed data show that the transformation effectively compensated the scale errors:

In [46]:
from pySurf.distanceTable import distanceTable
#import print_options
#print_options.set_float_precision(2)
print(distanceTable(np.array(m1)))
print('---------------------------')
print(distanceTable(np.array(mm2)))
print('---------------------------')
print(distanceTable(np.array(m1))-distanceTable(np.array(mm2)))
[[ 0.          9.14681873 28.77811282 33.75938797 41.47786781 71.81113922
  66.04822071 82.70248499]
 [ 9.14681873  0.         22.54812081 40.60471302 47.23866126 65.73629687
  59.01672244 83.17711529]
 [28.77811282 22.54812081  0.         45.6994495  48.08087093 43.26298992
  37.33212461 67.80699182]
 [33.75938797 40.60471302 45.6994495   0.          9.79777081 78.24507526
  77.44647199 62.79952237]
 [41.47786781 47.23866126 48.08087093  9.79777081  0.         75.5576039
  76.21559348 53.88942214]
 [71.81113922 65.73629687 43.26298992 78.24507526 75.5576039   0.
  11.66358039 59.02512114]
 [66.04822071 59.01672244 37.33212461 77.44647199 76.21559348 11.66358039
   0.         67.64829441]
 [82.70248499 83.17711529 67.80699182 62.79952237 53.88942214 59.02512114
  67.64829441  0.        ]]
---------------------------
[[ 0.          9.64382246 30.60149557 34.05087163 42.52085964 71.44869839
  66.53688849 83.11280816]
 [ 9.64382246  0.         23.7314451  41.03020074 48.09692604 64.7477607
  58.80732074 83.03102123]
 [30.60149557 23.7314451   0.         47.03454616 48.97605742 41.07703732
  36.05016982 66.82118676]
 [34.05087163 41.03020074 47.03454616  0.         11.10755042 78.00305934
  78.2758282  63.66261296]
 [42.52085964 48.09692604 48.97605742 11.10755042  0.         74.50323835
  76.4320432  53.43722014]
 [71.44869839 64.7477607  41.07703732 78.00305934 74.50323835  0.
  11.84863733 57.74161741]
 [66.53688849 58.80732074 36.05016982 78.2758282  76.4320432  11.84863733
   0.         67.29735636]
 [83.11280816 83.03102123 66.82118676 63.66261296 53.43722014 57.74161741
  67.29735636  0.        ]]
---------------------------
[[ 0.         -0.49700373 -1.82338275 -0.29148366 -1.04299183  0.36244083
  -0.48866777 -0.41032317]
 [-0.49700373  0.         -1.18332429 -0.42548772 -0.85826478  0.98853617
   0.2094017   0.14609406]
 [-1.82338275 -1.18332429  0.         -1.33509666 -0.89518649  2.1859526
   1.2819548   0.98580506]
 [-0.29148366 -0.42548772 -1.33509666  0.         -1.3097796   0.24201592
  -0.82935621 -0.86309058]
 [-1.04299183 -0.85826478 -0.89518649 -1.3097796   0.          1.05436555
  -0.21644973  0.452202  ]
 [ 0.36244083  0.98853617  2.1859526   0.24201592  1.05436555  0.
  -0.18505695  1.28350373]
 [-0.48866777  0.2094017   1.2819548  -0.82935621 -0.21644973 -0.18505695
   0.          0.35093805]
 [-0.41032317  0.14609406  0.98580506 -0.86309058  0.452202    1.28350373
   0.35093805  0.        ]]

The resulting net PZT effect (PZT shape - substrate shape) is shown below as surface and central axial profile.

In [10]:
def diffplot(p1t,p4):
    """plots and return difference"""
    plt.figure()
    maximize()

    ax1=plt.subplot(131)
    p1t.level((1,1)).plot()
    plt.title('PZT')
    plt.clim([-3,3])

    ax2=plt.subplot(132,sharex=ax1,sharey=ax1)
    p4.level((1,1)).plot()
    plt.title('substrate')
    plt.clim([-3,3])

    ax3=plt.subplot(133,sharex=ax1,sharey=ax1)
    diff=(p1t-p4).level((1,1))
    diff.name='PZT net effect (PZT-substrate)'
    diff.plot()
    plt.clim([-1,1])

    display(plt.gcf())
    return diff

diffPZT=diffplot(p2t,p1)
In [48]:
p1.save(os.path.join(outfolder,'wfs_substrate_aligned.dat'))
p2t.save(os.path.join(outfolder,'wfs_PZT_aligned.dat'))
diffPZT.save(os.path.join(outfolder,'figure_change_aligned.dat'))

plt.savefig(os.path.join(outfolder,'figure_change_aligned.png'))
In [49]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile(p1.topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(p2t.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='Substrate')
plt.plot(x2,y2,label='PZT')
plt.plot(xd,yd,label='Profile change')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('central axial profiles')
display(plt.gcf())
In [50]:
plt.savefig(os.path.join(outfolder,'data_central_profile.png'))

Fit PZT stress with VK's uniform stress simulation

Fit of stress is calculated starting fomr Vladimir Kradinov's simulation 2018/06/19 corresponding to a coating with uniform integrated stress 300 MPa um.

This is shown below alone and in comparison with data.

In [51]:
from pySurf.instrumentReader import points_reader, fitsWFS_reader
from pySurf.affine2D import plot_transform,find_rototrans
from pySurf.distanceTable import distanceTable
from pySurf.data2D_class import Data2D
from pySurf.data2D import resample_data
In [52]:
FEAf=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\Coating_stress\VK20180619\coat_300_MPa_um_ur.fits'  #300 MPa*um load
In [53]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=fitsWFS_reader(FEAf)  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=-fdata*1000000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
nx=fdata.shape[0]
scale=101.6/nx
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name='VK Simulation (cone)')
#rule is: corners have same sign as delta Radius

plt.clf()
df.plot()
display(plt.gcf())
df.save(fn_add_subfix(FEAf,'','.dat',strip=True,pre=outfolder+os.path.sep))
plt.savefig(fn_add_subfix(FEAf,'','.png',strip=True,pre=outfolder+os.path.sep))
In [54]:
fn_add_subfix(FEAf,'','.dat',strip=True,pre=outfolder+os.path.sep)
Out[54]:
'results\\C1S04_PZT_stress_fit\\coat_300_MPa_um_ur.dat'
In [55]:
p1=diffPZT
diff=p1-df  

plt.figure()
maximize()
ax1=plt.subplot(131)
p1.plot()

ax2=plt.subplot(132,sharex=ax1,sharey=ax1)
df.plot()

ax3=plt.subplot(133,sharex=ax1,sharey=ax1)
diff.plot()

display(plt.gcf())
In [56]:
plt.savefig(os.path.join(outfolder,'starting_data.png'))

DO FIT

In [57]:
outfolder
Out[57]:
'results\\C1S04_PZT_stress_fit'
In [58]:
from stress_compensation import comp2, scale_fit2, trioptimize, fom, fom_sl
import pandas as pd

Fit results

The best fit for stress is determined by raster scan of a scaling value for the FEA data and chosing the value that minimizes the rms of surface difference with PZT net effect. The raster scan is executed twice, coarse (no output) and finer (shown below), to determine the scale factor with two decimal digits.

The fit is repeated for different azimuthal apertures. For each of them it is shown:

  1. Value of the FOM for the variation of scale parameter
  2. Plot of surfaces and their difference on the selected region of interest (ROI)
  3. Plot of the entire surface full aperture

Each plot reports values for the ROI (small box) and for the entire surface (larger box) (the two are identical for the crop image at point 2)

Used functions (for who is interested in details).

In [59]:
print(help(trioptimize))
print('---------------------------')
print(help(scale_fit2))
print('---------------------------')
print(help(fom))
print('---------------------------')
print(help(fom_sl))
Help on function trioptimize in module stress_compensation:

trioptimize(d1, d2, fom, testvals=None, rois=None, outfile=None, noplot=False, dis=True)
    run optimizations with scale_fit2 for a set of cases
    corresponding to the values in matching lists of testvals (array of scaling test values) 
    and rois ([[xmin,xmax],[ymin,ymax]]). Returns a list of best scaling values for each case.
    
    d1 and d2 are Data2D objects.

None
---------------------------
Help on function scale_fit2 in module stress_compensation:

scale_fit2(d1, d2, testval, fom=<function fom at 0x0000025C13B0DB70>, outfile='', roi=None, crop=False, dis=True)
    p1 and df are Data2D objects. Returns value for scaling tbest of d2
    that minimizes fom(d1-d2*tbest). e.g. fom = rms of surface/slope.
    
    if `roi` is passed, fom is calculated on the roi only.
    `crop` flag controls the plotting. If True, only the roi is plotted and 
    color scale is adjusted accordingly. Otherwise, entire region is plotted and
    a rectangle around the roi is plotted.
    
    all fom and stats are calculated on leveled data, ideally I want to have stats in 
    roi unleveled, but it is safer this way for now.

None
---------------------------
Help on function fom in module stress_compensation:

fom(tdata, dy)
    Fit surface after plane level

None
---------------------------
Help on function fom_sl in module stress_compensation:

fom_sl(tdata, dy)
    Fit slope of surface after plane level

None
In [60]:
allres=pd.DataFrame()
In [61]:
os.makedirs(os.path.join(outfolder,'fits'),exist_ok=True)
In [62]:
#ROUGH FIT - set dis to True in call to show results

scales=trioptimize(p1,df,fom=fom,testvals=[np.arange(0.1,0.7,0.1),
                                  np.arange(0.1,0.7,0.1),
                                  np.arange(0.4,1.0,0.1),
                                  np.arange(0.4,1.0,0.1),
                                  np.arange(0.6,1.1,0.1)],
           rois=[[[-10,10],[-45,45]],[[-20,20],[-45,45]],[[-30,30],[-45,45]],
                 [[-37,37],[-45,45]],None],dis =False,
                  outfile=os.path.join(outfolder,'fits','wfs_test_roi'))
#allres = allres.append(scales)
scales
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in greater
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in less
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\dataIO\outliers.py:28: RuntimeWarning: invalid value encountered in less
  mask=mask & (np.abs(data-np.nanmean(data))<(nsigma*sigma))
=======================
Out[62]:
tbest testval roi rms_roi
wfs_test_roi 0.5 [0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6] [[-10, 10], [-45, 45]] 0.205929
wfs_test_roi 0.4 [0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6] [[-20, 20], [-45, 45]] 0.401271
wfs_test_roi 0.7 [0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.899... [[-30, 30], [-45, 45]] 0.674739
wfs_test_roi 0.8 [0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.899... [[-37, 37], [-45, 45]] 0.766546
wfs_test_roi 1.0 [0.6, 0.7, 0.7999999999999999, 0.8999999999999... None 0.800493
In [63]:
#FINE TUNING

allres=trioptimize(p1,df,fom=fom,testvals=[np.arange(0.45,0.55,0.01),
                                  np.arange(0.35,0.45,0.01),
                                  np.arange(0.7,0.8,0.01),
                                  np.arange(0.8,0.9,0.01),
                                  np.arange(0.95,1.05,0.01)],
           rois=[[[-10,10],[-45,45]],[[-20,20],[-45,45]],[[-30,30],[-45,45]],
                 [[-37,37],[-45,45]],None],
                  outfile=os.path.join(outfolder,'fits','wfs_test_roi'))
===

CROP: [-10.00:10.00] [-45.00:45.00]
TEST RANGE: [0.45 0.55]
unscaled fom: 0.8004934639960993
0 0.45 0.20666956806039755
1 0.46 0.20608969954707781
2 0.47000000000000003 0.2057249267726448
3 0.48000000000000004 0.20557639473170813
4 0.49000000000000005 0.20564457198096872
5 0.5 0.2059292432819009
6 0.51 0.20642951298481657
7 0.52 0.2071438190309152
8 0.53 0.20806995706576256
9 0.54 0.2092051138071337
10 0.55 0.21054590851364216
BEST SCALE: 0.480

===

CROP: [-20.00:20.00] [-45.00:45.00]
TEST RANGE: [0.35 0.45]
unscaled fom: 0.8004934639960993
0 0.35 0.4026396589837692
1 0.36 0.4022294447958274
2 0.37 0.40188729286149527
3 0.38 0.4016133771364084
4 0.39 0.4014078373112174
5 0.4 0.40127077845690995
6 0.41000000000000003 0.4012022707569692
7 0.42000000000000004 0.40120234932795484
8 0.43000000000000005 0.4012710141295784
9 0.44000000000000006 0.4014082299648057
10 0.45000000000000007 0.40161392656998884
BEST SCALE: 0.410

===

CROP: [-30.00:30.00] [-45.00:45.00]
TEST RANGE: [0.7 0.8]
unscaled fom: 0.8004934639960993
0 0.7 0.6747394871725482
1 0.71 0.6745738469682369
2 0.72 0.6744611390276413
3 0.73 0.6744013898893482
4 0.74 0.6743946136292831
5 0.75 0.6744408118441378
6 0.76 0.6745399736494891
7 0.77 0.6746920756926166
8 0.78 0.6748970821799822
9 0.79 0.6751549449192978
10 0.8 0.6754656033760642
BEST SCALE: 0.740

===

CROP: [-37.00:37.00] [-45.00:45.00]
TEST RANGE: [0.8  0.89]
unscaled fom: 0.8004934639960993
0 0.8 0.7665463213938697
1 0.81 0.7662064323844874
2 0.8200000000000001 0.7659537160184914
3 0.8300000000000001 0.7657882585989346
4 0.8400000000000001 0.7657101166915082
5 0.8500000000000001 0.7657193170279427
6 0.8600000000000001 0.7658158564603087
7 0.8700000000000001 0.765999701966401
8 0.8800000000000001 0.7662707907061805
9 0.8900000000000001 0.7666290301290556
BEST SCALE: 0.840

===

FULL APERTURE
TEST RANGE: [0.95 1.05]
unscaled fom: 0.8004934639960993
0 0.95 0.8083076419282468
1 0.96 0.8059842049115856
2 0.97 0.8040385811891909
3 0.98 0.8024735188284416
4 0.99 0.8012912477417208
5 1.0 0.8004934639960993
6 1.01 0.8000813177420611
7 1.02 0.8000554049599815
8 1.03 0.8004157631618051
9 1.04 0.8011618711200116
10 1.05 0.8022926526284218
BEST SCALE: 1.020

===
SUMMARY:
CROP: [-10.00:10.00] [-45.00:45.00] --> BEST SCALE:  0.48000000000000004
CROP: [-20.00:20.00] [-45.00:45.00] --> BEST SCALE:  0.41000000000000003
CROP: [-30.00:30.00] [-45.00:45.00] --> BEST SCALE:  0.74
CROP: [-37.00:37.00] [-45.00:45.00] --> BEST SCALE:  0.8400000000000001
FULL APERTURE--> BEST SCALE:  1.02
=======================
In [64]:
allres[['tbest','rms_roi','roi']]
Out[64]:
tbest rms_roi roi
wfs_test_roi 0.48 0.205576 [[-10, 10], [-45, 45]]
wfs_test_roi 0.41 0.401202 [[-20, 20], [-45, 45]]
wfs_test_roi 0.74 0.674395 [[-30, 30], [-45, 45]]
wfs_test_roi 0.84 0.765710 [[-37, 37], [-45, 45]]
wfs_test_roi 1.02 0.800055 None
In [65]:
allres[['tbest','rms_roi','roi']].to_csv(os.path.join(outfolder,'all_stats.txt'))

Plot Best

In [66]:
plt.close('all')
comp2(p1,df*1.02)
plt.suptitle('best factor = 1.02')
display(plt.gcf())
In [67]:
plt.savefig(os.path.join(outfolder,'best_fit_VK.png'))

REPEAT FIT USING VM STRESS DATA

The exact same procedure is repeated on a different set of data by Vanessa Marquez, representing a PZT film on back of substrate, having an integrated stress of 150 MPa um. It is then expected to find values that are about double of previous ones.

In [68]:
from pySurf.instrumentReader import FEAreader
In [69]:
FEAf2=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\CoatingStress_Analysis\220mm_CoatingStress\Primary\220mmROC_Case1_MirrorSurfaceOnly.csv'
In [70]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=points_autoresample(FEAreader(FEAf2))  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=fdata*1000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
nx=fdata.shape[0]
scale=1.016
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name='VM simulation 220mmROC_Case1_MirrorSurfaceOnly')
#rule is: corners have same sign as delta Radius

plt.clf()
df.plot()
display(plt.gcf())
df.save(fn_add_subfix(FEAf2,'','.dat',strip=True,pre=outfolder+os.path.sep))
plt.savefig(fn_add_subfix(FEAf2,'','.png',strip=True,pre=outfolder+os.path.sep))
WARNING: points number doesn't match regular grid for size determined by points_find_grid
WARNING: points number doesn't match regular grid for size determined by points_find_grid
In [71]:
os.makedirs(os.path.join(outfolder,'fits2'),exist_ok=True)
In [72]:
#ROUGH FIT

scales=trioptimize(p1,df,fom=fom,testvals=[np.arange(0.8,1.3,0.1),
                                  np.arange(0.5,1.1,0.1),
                                  np.arange(0.8,1.4,0.1),
                                  np.arange(1.3,1.9,0.1),
                                  np.arange(1.8,2.4,0.1)],
           rois=[[[-10,10],[-45,45]],[[-20,20],[-45,45]],[[-30,30],[-45,45]],
                 [[-37,37],[-45,45]],None],dis=False,
                  outfile=os.path.join(outfolder,'fits2','wfs_test_roi'))
#allres = allres.append(scales)
scales
=======================
Out[72]:
tbest testval roi rms_roi
wfs_test_roi 0.9 [0.8, 0.9, 1.0, 1.1, 1.2] [[-10, 10], [-45, 45]] 0.212270
wfs_test_roi 0.7 [0.5, 0.6, 0.7, 0.7999999999999999, 0.89999999... [[-20, 20], [-45, 45]] 0.417399
wfs_test_roi 1.1 [0.8, 0.9, 1.0, 1.1, 1.2, 1.2999999999999998] [[-30, 30], [-45, 45]] 0.722257
wfs_test_roi 1.4 [1.3, 1.4000000000000001, 1.5000000000000002, ... [[-37, 37], [-45, 45]] 0.830354
wfs_test_roi 1.9 [1.8, 1.9000000000000001, 2.0, 2.1000000000000... None 0.878715
In [73]:
#FINE TUNING

scales=trioptimize(p1,df,fom=fom,testvals=[np.arange(0.88,0.92,0.01),
                                  np.arange(0.64,0.71,0.01),
                                  np.arange(1.09,1.14,0.01),
                                  np.arange(1.38,1.42,0.01),
                                  np.arange(1.92,2.0,0.01)],
           rois=[[[-10,10],[-45,45]],[[-20,20],[-45,45]],[[-30,30],[-45,45]],
                 [[-37,37],[-45,45]],None],
                  outfile=os.path.join(outfolder,'fits2','wfs_test_roi'))
allres=allres.append(scales)
===

CROP: [-10.00:10.00] [-45.00:45.00]
TEST RANGE: [0.88 0.92]
unscaled fom: 1.2185415385392677
0 0.88 0.2124179753333993
1 0.89 0.21231473896270084
2 0.9 0.2122696557830656
3 0.91 0.21228276284504285
4 0.92 0.21235404937365912
BEST SCALE: 0.900

===

CROP: [-20.00:20.00] [-45.00:45.00]
TEST RANGE: [0.64 0.7 ]
unscaled fom: 1.2185415385392677
0 0.64 0.41735072561486447
1 0.65 0.4173124449846069
2 0.66 0.4172926656152855
3 0.67 0.4172913901377473
4 0.68 0.4173086187216602
5 0.6900000000000001 0.4173443490754002
6 0.7000000000000001 0.41739857644757533
BEST SCALE: 0.670

===

CROP: [-30.00:30.00] [-45.00:45.00]
TEST RANGE: [1.09 1.13]
unscaled fom: 1.2185415385392677
0 1.09 0.7222763047157712
1 1.1 0.7222568796593288
2 1.11 0.7222517431164446
3 1.12 0.7222608953919675
4 1.1300000000000001 0.7222843359427246
BEST SCALE: 1.110

===

CROP: [-37.00:37.00] [-45.00:45.00]
TEST RANGE: [1.38 1.42]
unscaled fom: 1.2185415385392677
0 1.38 0.8304196731366982
1 1.39 0.8303753023637096
2 1.4 0.8303537134877286
3 1.41 0.8303549082857145
4 1.42 0.8303788866593196
BEST SCALE: 1.400

===

FULL APERTURE
TEST RANGE: [1.92 2.  ]
unscaled fom: 1.2185415385392677
0 1.92 0.8781271645217286
1 1.93 0.8779707351867994
2 1.94 0.8779059389972694
3 1.95 0.8779327962422045
4 1.96 0.8780512985113033
5 1.97 0.8782614087080598
6 1.98 0.8785630611077934
7 1.99 0.8789561614603296
8 2.0 0.8794405871369644
BEST SCALE: 1.940

===
SUMMARY:
CROP: [-10.00:10.00] [-45.00:45.00] --> BEST SCALE:  0.9
CROP: [-20.00:20.00] [-45.00:45.00] --> BEST SCALE:  0.67
CROP: [-30.00:30.00] [-45.00:45.00] --> BEST SCALE:  1.11
CROP: [-37.00:37.00] [-45.00:45.00] --> BEST SCALE:  1.4
FULL APERTURE--> BEST SCALE:  1.94
=======================

This is a summary of results (first half from VK's simulation, 300 MPa um, second half for VM's, 150 MPa um)

In [74]:
allres[['tbest','rms_roi','roi']]
Out[74]:
tbest rms_roi roi
wfs_test_roi 0.48 0.205576 [[-10, 10], [-45, 45]]
wfs_test_roi 0.41 0.401202 [[-20, 20], [-45, 45]]
wfs_test_roi 0.74 0.674395 [[-30, 30], [-45, 45]]
wfs_test_roi 0.84 0.765710 [[-37, 37], [-45, 45]]
wfs_test_roi 1.02 0.800055 None
wfs_test_roi 0.90 0.212270 [[-10, 10], [-45, 45]]
wfs_test_roi 0.67 0.417291 [[-20, 20], [-45, 45]]
wfs_test_roi 1.11 0.722252 [[-30, 30], [-45, 45]]
wfs_test_roi 1.40 0.830354 [[-37, 37], [-45, 45]]
wfs_test_roi 1.94 0.877906 None
In [75]:
allres[['tbest','rms_roi','roi']].to_csv(os.path.join(outfolder,'all_stats_VKVM.txt'))

Plot Best VM

In [76]:
bf=1.94
plt.close('all')
comp2(p1,df*bf)
plt.suptitle('best factor = %F.2'%bf)
display(plt.gcf())
In [77]:
plt.savefig(os.path.join(outfolder,'best_fit_VM.png'))
In [ ]:
 

Compare finite element simulations

In [37]:
from pySurf.instrumentReader import points_reader, fitsWFS_reader, FEAreader
from pySurf.affine2D import plot_transform,find_rototrans
from pySurf.distanceTable import distanceTable
from pySurf.data2D_class import Data2D
from pySurf.data2D import resample_data
In [38]:
FEAf=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\Coating_stress\VK20180619\coat_300_MPa_um_ur.fits'  #300 MPa*um load
In [39]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=fitsWFS_reader(FEAf)  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=-fdata*1000000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
nx=fdata.shape[0]
scale=101.6/nx
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name='VK Simulation (cone)')
#rule is: corners have same sign as delta Radius
In [40]:
FEAf2=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\CoatingStress_Analysis\220mm_CoatingStress\Primary\220mmROC_Case1_MirrorSurfaceOnly.csv'
In [44]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=points_autoresample(FEAreader(FEAf2))  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=fdata*1000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
nx=fdata.shape[0]
scale=1.016
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df2=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name='VM simulation 220mmROC_Case1_MirrorSurfaceOnly')
#rule is: corners have same sign as delta Radius
WARNING: points number doesn't match regular grid for size determined by points_find_grid
WARNING: points number doesn't match regular grid for size determined by points_find_grid
In [45]:
plt.close('all')
df=df.crop([-45,45],[-45,45]).level()
df.plot()
display(plt.gcf())
In [46]:
plt.figure()
df2=df2.crop([-45,45],[-45,45]).level()
df2.plot()
display(plt.gcf())
In [57]:
plt.figure()
maximize()

ax1=plt.subplot(131)
df.plot(title='VK')

plt.subplot(132,sharex=ax1,sharey=ax1)
(df2).plot(title='VM')


plt.subplot(133,sharex=ax1,sharey=ax1)
(df-df2*2).plot(title='VK-VM*2')

display(plt.gcf())
plt.savefig(os.path.join(outfolder,'FEAdiff_crop.png'))
In [58]:
plt.close('all')
In [ ]: